package framework;

import java.awt.BorderLayout;
import java.awt.FlowLayout;
import java.awt.GridLayout;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;

import javax.swing.JButton;
import javax.swing.JFrame;
import javax.swing.JLabel;
import javax.swing.JPanel;

/**
 * Main gui class that user must create
 * 
 * @author jtumarki, ntibrewa, shuaihan, wesleyk
 * 
 */
public class MainGUI implements ActionListener, Runnable {
	// board representing
	private Board mainBoard;

	// single instance of MainGUI
	// using Singleton design pattern
	private AutomatonPlugin ap;
	private VisualizationPlugin vp;

	private int width;
	private int height;
	private JPanel graph;
	private JPanel buttons;
	private JButton stepButton;
	private JButton startButton;
	private JButton stopButton;

	private JFrame myFrame;

	private JLabel[][] allLabels;

	private boolean isRunning = false;

	/**
	 * Constructor for MainGUI
	 * 
	 * @param v
	 *            VisualizationPlugin that client passes in
	 * @param a
	 *            AutomatonPlugin that client passes in
	 */
	public MainGUI(AutomatonPlugin a, VisualizationPlugin v) {

		myFrame = new JFrame("Conway Demo");
		ap = a;
		vp = v;

		mainBoard = a.initialBoard();
		width = mainBoard.getWidth();
		height = mainBoard.getHeight();

		// main graph/board initialization
		graph = new JPanel();
		graph.setLayout(new GridLayout(height, width));
		stepButton = new JButton("step");
		stepButton.addActionListener(this);
		startButton = new JButton("start");
		startButton.addActionListener(this);
		stopButton = new JButton("stop");
		stopButton.addActionListener(this);

		initializeLabel();

		myFrame.setLayout(new BorderLayout());
		myFrame.add(graph, BorderLayout.NORTH);

		// button initialization
		buttons = new JPanel();
		buttons.setLayout(new FlowLayout());
		buttons.add(stepButton);
		buttons.add(startButton);
		buttons.add(stopButton);

		myFrame.add(buttons, BorderLayout.SOUTH);

	}

	public void show() {
		myFrame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
		myFrame.setTitle("Demo");
		myFrame.setResizable(false);
		myFrame.pack();
		myFrame.setVisible(true);
	}

	/**
	 * Method used to display and set initial board
	 */
	private void initializeLabel() {
		allLabels = new JLabel[height][width];
		JLabel myLabel;
		State state;
		Visual vs;
		for (int i = 0; i < height; i++) {
			for (int j = 0; j < width; j++) {
				state = mainBoard.getState(i, j);
				vs = vp.getVisual(state);
				myLabel = new JLabel(vs.getSymbol());
				allLabels[i][j] = myLabel;
				graph.add(myLabel);
			}
		}
	}

	/**
	 * Method used to display main board components
	 */
	private void display() {

		for (int i = 0; i < height; i++) {
			for (int j = 0; j < width; j++) {
				State state = mainBoard.getState(i, j);
				Visual v = vp.getVisual(state);
				allLabels[i][j].setIcon(v.getSymbol());
			}
		}
	}

	/**
	 * Step to next state for full board
	 */
	private void step() {
		State state;
		for (int i = 0; i < height; i++) {
			for (int j = 0; j < height; j++) {
				state = ap.getStateCalculator().getNextState(i, j, mainBoard);
				mainBoard.setState(state, i, j);
			}
		}
	}

	/**
	 * The action performed function for when the step button is pressed. It
	 * will essentially call the necessary methods to change the board to the
	 * next state.
	 * 
	 * @param ae
	 *            Action event
	 */
	public void actionPerformed(ActionEvent ae) {

		if (ae.getSource() == stepButton) {
			step();
			display();
		}// end if
		else if (ae.getSource() == startButton) {

			isRunning = true;
			Thread t = new Thread(this);
			t.start();

		}// end if
		else if (ae.getSource() == stopButton) {

			stop();

		}// end if
	}

	private boolean stepThread() {

		step();
		display();
		sleep();

		return true;
	}

	private void stop() {
		// value breaks concurrent loop in run()
		isRunning = false;
	}

	@Override
	public void run() {
		// TODO Runs forever
		if (isRunning) {
			while (isRunning && stepThread())
				;
		}

	}

	/**
	 * this method will cause the current thread to sleep for 1000 milliseconds
	 */
	private void sleep() {
		try {
			Thread.sleep(500);
		} catch (InterruptedException e) {
			e.printStackTrace();
		}
	}
}